﻿using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace GameFlowManager.Screens
{
	public abstract class GameScreen
	{
		#region Variables

		/// <summary>
		/// Start time in seconds.
		/// </summary>
		/// <remarks>
		/// This is set in the first call to Draw.
		/// </remarks>
		private double? _startTime;

		/// <summary>
		/// The time at which to transition off.
		/// </summary>
		private double? _endTime;

		private bool _beginExiting;

		private SpriteBatch _spriteBatch;

		/// <summary>
		/// Game components that should be unloaded upon exiting this screen.
		/// </summary>
		private List<IGameComponent> _screenComponents;

		#endregion

		#region Constructors

		public GameScreen()
		{
			TransitionOnTime = 0.0f;
			TransitionOffTime = 0.0f;
			_startTime = null;
			_endTime = null;
			_beginExiting = false;
			_screenComponents = new List<IGameComponent>();
		}

		#endregion

		#region Properties

		public bool IsActive
		{
			get
			{
				return Manager.ActiveScreen == this;
			}
		}

		public float TransitionOnTime { get; set; }

		public float TransitionOffTime { get; set; }

		public IScreenManager Manager { get; set; }

		protected static Texture2D BlankTexture { get; set; }

		protected static Rectangle Viewport { get; set; }

		#endregion

		#region Methods

		public T LoadComponent<T>(T component)
			where T : class, IGameComponent
		{
			Manager.Game.Components.Add(component);
			_screenComponents.Add(component);

			return component;
		}

		public virtual void LoadContent()
		{
			if (BlankTexture == null)
			{
				BlankTexture = new Texture2D(Manager.Game.GraphicsDevice, 1, 1);
				BlankTexture.SetData<Color>(new[] { Color.Black });
				Viewport = new Rectangle(0, 0, Manager.Game.GraphicsDevice.Viewport.Width, Manager.Game.GraphicsDevice.Viewport.Height);
			}

			_spriteBatch = new SpriteBatch(Manager.Game.GraphicsDevice);
		}

		public virtual void UnloadContent()
		{
			_screenComponents.ForEach(x => Manager.Game.Components.Remove(x));
			_screenComponents.Clear();
			Manager = null;
		}

		public virtual void Update(GameTime gameTime)
		{
		}

		public virtual void Draw(GameTime gameTime)
		{
			if (!_startTime.HasValue)
			{
				_startTime = gameTime.TotalGameTime.TotalSeconds;
			}
			if (_beginExiting && !_endTime.HasValue)
			{
				_endTime = gameTime.TotalGameTime.TotalSeconds;
			}

			switch (GetState(gameTime))
			{
				case ScreenState.TransitionOn:
					_spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
					_spriteBatch.Draw(BlankTexture, new Rectangle(0, 0, Viewport.Width, Viewport.Height),
						new Color(0.0f, 0.0f, 0.0f, 1.0f - (float)(gameTime.TotalGameTime.TotalSeconds - _startTime.Value) / TransitionOnTime));
					_spriteBatch.End();
					break;
				case ScreenState.TransitionOff:
					_spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
					_spriteBatch.Draw(BlankTexture, new Rectangle(0, 0, Viewport.Width, Viewport.Height),
						new Color(0.0f, 0.0f, 0.0f, (float)(gameTime.TotalGameTime.TotalSeconds - _endTime.Value) / TransitionOnTime));
					_spriteBatch.End();
					break;
			}
		}

		public ScreenState GetState(GameTime gameTime)
		{
			if (!_startTime.HasValue || (gameTime.TotalGameTime.TotalSeconds - _startTime.Value <= TransitionOnTime))
			{
				return ScreenState.TransitionOn;
			}
			else if (_endTime.HasValue)
			{
				if (gameTime.TotalGameTime.TotalSeconds - _endTime.Value <= TransitionOffTime)
				{
					return ScreenState.TransitionOff;
				}
				else
				{
					return ScreenState.Exit;
				}
			}
			else
			{
				return ScreenState.Play;
			}
		}

		/// <summary>
		/// Transition off this screen.
		/// </summary>
		public void ExitScreen()
		{
			_beginExiting = true;
		}

		#region IScreenManager Proxy

		public void EnterScreen(GameScreen screen)
		{
			Manager.EnterScreen(screen);
		}

		public void LeaveScreen()
		{
			Manager.LeaveScreen();
		}

		public void SwitchScreens(GameScreen screen)
		{
			Manager.SwitchScreens(screen);
		}

		#endregion

		#endregion
	}
}